iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Rust

把前端加速到天花板:Rust+WASM 即插即用外掛系列 第 21

Day 21|Node 端策略(支援,或是乾脆不支援)

  • 分享至 

  • xImage
  •  

到目前為止,我們一直活在「瀏覽器世界」:
window、有 canvas、有 performance.now()、有 Web Worker。
但今天如果有人在 npm 上裝了我們的套件,開在 Node 裡呼叫會發生什麼?

答案通常是:「能編譯,但跑不起來。」

這就引出一個設計哲學問題——
WASM 模組要不要支援 Node?
如果支援,要支援到什麼程度?
如果不支援,要怎麼「優雅地拒絕」?

為什麼 Node 很麻煩?

在 Node 環境下:

  1. 沒有 fetch() 去抓 .wasm(除非你 polyfill 或改用 fs.readFileSync);
  2. 沒有 DOM,所以拿不到 ImageDataCanvasRenderingContext2D
  3. 沒有 window.performancealert 等 Web 對象;
  4. 而且最關鍵:很多 bundler(Vite、Webpack)在 target: node 下會自動改 loader 行為,導致 .wasm?url 找不到。

所以,要讓它「能跑」,你必須多包一層。
要不然就是要讓它「明確的失敗」。

方法一:正式支援 Node

若真的要支援,可以走以下策略:

  1. 使用 wasm-bindgen 的 Node 目標
    --target nodejs 會產生 CommonJS 封裝:

    wasm-pack build --target nodejs --out-dir pkg-node
    

    它會把 .wasm 路徑直接內嵌成 fs.readFileSync(__dirname + '/xxx_bg.wasm')
    在 Node 中直接:

    import init, { apply_pipeline } from 'img-wasm-node'
    await init()
    const out = apply_pipeline(bytes, w, h, ops)
    
  2. 輸出雙目標pkg/ 給 web,pkg-node/ 給 Node。
    然後在 package.json 宣告:

    {
      "exports": {
        "import": "./pkg/img_wasm.js",
        "require": "./pkg-node/img_wasm.js"
      }
    }
    

    如此 Node 跑 require() 時自動導向 node 版。

  3. 額外提供 I/O 工具
    沒有 canvas?可以用 sharppngjs 幫忙載圖、轉成 RGBA,再餵給 apply_pipeline()

    import { PNG } from 'pngjs'
    import fs from 'fs'
    import { apply_pipeline } from 'img-wasm-node'
    
    const png = PNG.sync.read(fs.readFileSync('input.png'))
    const out = apply_pipeline(png.data, png.width, png.height, [{ kind: 'grayscale' }])
    png.data = out
    fs.writeFileSync('output.png', PNG.sync.write(png))
    

這樣整套從頭到尾就能在 Node 環境裡跑起來,適合批次影像處理或 CLI 工具。

方法二:乾脆不支援

另一派是:明確宣告「只支援瀏覽器」,並讓錯誤在第一時間爆出來,而不是模糊地掛掉。

在入口加一句保險:

#[wasm_bindgen(start)]
pub fn check_env() -> Result<(), JsValue> {
    if js_sys::global().has("window") {
        Ok(())
    } else {
        Err(js_err(ErrorCode::Unsupported, "This build only runs in browser (no window object detected)"))
    }
}

這樣使用者在 Node 裡呼叫時會馬上收到一個乾淨的錯誤:

{ "code": "UNSUPPORTED", "message": "This build only runs in browser (no window object detected)" }

而不是一堆奇怪的「ReferenceError: window is not defined」。

哪個比較好?

  • 要開放給 CLI / Server-Side 工具使用 → 支援 Node。
    (需要導出 fs 版初始化邏輯與 pngjs 範例)
  • 專心給前端或 WebAssembly playground 用 → 明確拒絕。
    (加防呆、加錯誤提示、別讓使用者誤會能在 Node 跑)

最怕的是第三種:死的不明不白

如果你能跑,就讓它跑得順;
如果不能跑,就讓它死得乾脆。


上一篇
Day 20|首頁活化
下一篇
Day 22|手機?
系列文
把前端加速到天花板:Rust+WASM 即插即用外掛26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言